package com.dankegongyu.lib.map.baidu;

import android.annotation.SuppressLint;

import com.baidu.mapapi.model.LatLng;
import com.baidu.mapapi.search.core.RouteLine;
import com.baidu.mapapi.search.core.SearchResult;
import com.baidu.mapapi.search.route.BikingRoutePlanOption;
import com.baidu.mapapi.search.route.BikingRouteResult;
import com.baidu.mapapi.search.route.DrivingRoutePlanOption;
import com.baidu.mapapi.search.route.DrivingRouteResult;
import com.baidu.mapapi.search.route.IndoorRoutePlanOption;
import com.baidu.mapapi.search.route.IndoorRouteResult;
import com.baidu.mapapi.search.route.MassTransitRoutePlanOption;
import com.baidu.mapapi.search.route.MassTransitRouteResult;
import com.baidu.mapapi.search.route.OnGetRoutePlanResultListener;
import com.baidu.mapapi.search.route.PlanNode;
import com.baidu.mapapi.search.route.RoutePlanSearch;
import com.baidu.mapapi.search.route.TransitRoutePlanOption;
import com.baidu.mapapi.search.route.TransitRouteResult;
import com.baidu.mapapi.search.route.WalkingRoutePlanOption;
import com.baidu.mapapi.search.route.WalkingRouteResult;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

/**
 * 路线规划 http://lbsyun.baidu.com/index.php?title=androidsdk/guide/route/transit
 *
 * @author wupuquan
 * @version 1.0
 * @since 2019/2/28 16:16
 */
@SuppressWarnings({"WeakerAccess", "unused", "ConstantConditions"})
public class DKMapRoutePlan {

    private RoutePlanSearch mSearch;
    private OnRoutePlanResultListener mResultListener;

    private DKMapRoutePlan() {
        mSearch = RoutePlanSearch.newInstance();
        mSearch.setOnGetRoutePlanResultListener(onGetRoutePlanResultListener);
    }

    public static DKMapRoutePlan newInstance() {
        return new DKMapRoutePlan();
    }

    private OnGetRoutePlanResultListener onGetRoutePlanResultListener = new OnGetRoutePlanResultListener() {
        @Override
        public void onGetWalkingRouteResult(WalkingRouteResult result) {
            boolean success = isResultSuccess(result);
            if (mResultListener != null) {
                mResultListener.onWalkingRouteResult(success, result);
            }
            if (success && result != null) {
                handleShortestDurationRoute(WALKING, result.getRouteLines());
            }
        }

        @Override
        public void onGetBikingRouteResult(BikingRouteResult result) {
            boolean success = isResultSuccess(result);
            if (mResultListener != null) {
                mResultListener.onBikingRouteResult(success, result);
            }
            if (success && result != null) {
                handleShortestDurationRoute(BIKING, result.getRouteLines());
            }
        }

        @Override
        public void onGetDrivingRouteResult(DrivingRouteResult result) {
            boolean success = isResultSuccess(result);
            if (mResultListener != null) {
                mResultListener.onDrivingRouteResult(success, result);
            }
            if (success && result != null) {
                handleShortestDurationRoute(DRIVING, result.getRouteLines());
            }
        }

        @Override
        public void onGetMassTransitRouteResult(MassTransitRouteResult result) {
            boolean success = isResultSuccess(result);
            if (mResultListener != null) {
                mResultListener.onMassTransitRouteResult(success, result);
            }
            if (success && result != null) {
                handleShortestDurationRoute(MASS_TRANSIT, result.getRouteLines());
            }
        }

        @Override
        public void onGetTransitRouteResult(TransitRouteResult result) {
            boolean success = isResultSuccess(result);
            if (mResultListener != null) {
                mResultListener.onTransitRouteResult(success, result);
            }
        }

        @Override
        public void onGetIndoorRouteResult(IndoorRouteResult result) {
            boolean success = isResultSuccess(result);
            if (mResultListener != null) {
                mResultListener.onIndoorRouteResult(success, result);
            }
        }

        /**
         * 获取时间最短的路径
         * @param list 路径集
         * @return 时间最短的路径
         */
        private <T extends RouteLine> void handleShortestDurationRoute(@RoutePlanMode int mode, List<T> list) {
            if (list == null || list.isEmpty()) {
                return;
            }
            if (mShortestDurationResultMap != null && !mShortestDurationResultMap.isEmpty()) {
                OnShortestDurationResultListener resultListener = mShortestDurationResultMap.get(mode);
                if (resultListener != null) {
                    T minDurationRoute = getShortestDurationRoute(list);
                    if (minDurationRoute != null) {
                        resultListener.onShortestDurationResult(minDurationRoute);
                    }
                }
            }
        }
    };

    public static boolean isResultSuccess(SearchResult result) {
        return result != null && result.error == SearchResult.ERRORNO.NO_ERROR;
    }

    @Nullable
    public static <T extends RouteLine> T getShortestDurationRoute(List<T> list) {
        if (list == null || list.isEmpty()) {
            return null;
        }
        T minDurationRoute = null;
        for (T routeLine : list) {
            if (minDurationRoute == null) {
                minDurationRoute = routeLine;
            }
            if (routeLine.getDuration() < minDurationRoute.getDuration()) {
                minDurationRoute = routeLine;
            }
        }
        return minDurationRoute;
    }

    public void setOnRoutePlanResultListener(OnRoutePlanResultListener onRoutePlanResultListener) {
        if (onRoutePlanResultListener != null) {
            this.mResultListener = onRoutePlanResultListener;
        }
    }

    public static class OnRoutePlanResultListener {

        /**
         * 步行结果
         *
         * @param success 结果是否有效
         * @param result  步行结果
         */
        public void onWalkingRouteResult(boolean success, WalkingRouteResult result) {

        }

        /**
         * 骑行结果
         *
         * @param success 结果是否有效
         * @param result  骑行结果
         */
        public void onBikingRouteResult(boolean success, BikingRouteResult result) {

        }

        /**
         * 驾车结果
         *
         * @param success 结果是否有效
         * @param result  驾车结果
         */
        public void onDrivingRouteResult(boolean success, DrivingRouteResult result) {

        }

        /**
         * 跨城公交结果
         *
         * @param success 结果是否有效
         * @param result  跨城公交结果
         */
        public void onMassTransitRouteResult(boolean success, MassTransitRouteResult result) {

        }

        /**
         * 市内公交结果
         *
         * @param success 结果是否有效
         * @param result  市内公交结果
         */
        public void onTransitRouteResult(boolean success, TransitRouteResult result) {

        }

        /**
         * 室内结果
         *
         * @param success 结果是否有效
         * @param result  室内结果
         */
        public void onIndoorRouteResult(boolean success, IndoorRouteResult result) {

        }
    }

    /**
     * 步行路径规划
     *
     * @param walkingRoutePlanOption 步行参数
     */
    public void walkingSearch(WalkingRoutePlanOption walkingRoutePlanOption) {
        if (mSearch != null) {
            mSearch.walkingSearch(walkingRoutePlanOption);
        }
    }

    /**
     * 骑行路径规划
     *
     * @param bikingRoutePlanOption 骑行参数
     */
    public void bikingSearch(BikingRoutePlanOption bikingRoutePlanOption) {
        if (mSearch != null) {
            mSearch.bikingSearch(bikingRoutePlanOption);
        }
    }

    /**
     * 驾车路径规划
     *
     * @param drivingRoutePlanOption 驾车参数
     */
    public void drivingSearch(DrivingRoutePlanOption drivingRoutePlanOption) {
        if (mSearch != null) {
            mSearch.drivingSearch(drivingRoutePlanOption);
        }
    }

    /**
     * 跨城公交路径规划
     *
     * @param massTransitRoutePlanOption 跨城公交参数
     */
    public void masstransitSearch(MassTransitRoutePlanOption massTransitRoutePlanOption) {
        if (mSearch != null) {
            mSearch.masstransitSearch(massTransitRoutePlanOption);
        }
    }

    /**
     * 市内公交路径规划
     *
     * @param transitRoutePlanOption 市内公交参数
     */
    public void transitSearch(TransitRoutePlanOption transitRoutePlanOption) {
        if (mSearch != null) {
            mSearch.transitSearch(transitRoutePlanOption);
        }
    }

    /**
     * 室内路径规划
     *
     * @param indoorRoutePlanOption 室内参数
     */
    public void walkingIndoorSearch(IndoorRoutePlanOption indoorRoutePlanOption) {
        if (mSearch != null) {
            mSearch.walkingIndoorSearch(indoorRoutePlanOption);
        }
    }

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({WALKING, BIKING, DRIVING, MASS_TRANSIT})
    public @interface RoutePlanMode {
    }

    /**
     * 步行
     */
    public static final int WALKING = 0;
    /**
     * 骑行
     */
    public static final int BIKING = 1;
    /**
     * 驾车
     */
    public static final int DRIVING = 2;
    /**
     * 跨城公交
     */
    public static final int MASS_TRANSIT = 3;

    /**
     * 简单的路径规划，以经纬度为参考
     *
     * @param mode    {@link RoutePlanMode}
     * @param latFrom 起点纬度
     * @param lngFrom 起点经度
     * @param latTo   终点纬度
     * @param lngTo   终点经度
     */
    public void simpleSearch(@RoutePlanMode int mode, double latFrom, double lngFrom, double latTo, double lngTo) {
        PlanNode nodeFrom = PlanNode.withLocation(new LatLng(latFrom, lngFrom));
        PlanNode nodeTo = PlanNode.withLocation(new LatLng(latTo, lngTo));
        simpleSearch(mode, nodeFrom, nodeTo);
    }

    /**
     * 简单的路径规划，以经纬度为参考
     *
     * @param mode {@link RoutePlanMode}
     * @param from 起点
     * @param to   终点
     */
    public void simpleSearch(@RoutePlanMode int mode, PlanNode from, PlanNode to) {
        if (from == null || to == null || mSearch == null) {
            return;
        }
        switch (mode) {
            case WALKING:
                mSearch.walkingSearch(new WalkingRoutePlanOption().from(from).to(to));
                break;
            case BIKING:
                mSearch.bikingSearch(new BikingRoutePlanOption().from(from).to(to));
                break;
            case DRIVING:
                mSearch.drivingSearch(new DrivingRoutePlanOption().from(from).to(to));
                break;
            case MASS_TRANSIT:
                mSearch.masstransitSearch(new MassTransitRoutePlanOption().from(from).to(to));
                break;
        }
    }

    @SuppressLint("UseSparseArrays")
    private Map<Integer, OnShortestDurationResultListener> mShortestDurationResultMap = new HashMap<>();

    /**
     * 搜索时间最短的路径
     *
     * @param mode    {@link RoutePlanMode}
     * @param latFrom 起点纬度
     * @param lngFrom 起点经度
     * @param latTo   终点纬度
     * @param lngTo   终点经度
     */
    public void searchShortestDurationRoute(@RoutePlanMode int mode, double latFrom, double lngFrom, double latTo, double lngTo, OnShortestDurationResultListener onShortestDurationResultListener) {
        if (mSearch == null || onShortestDurationResultListener == null) {
            return;
        }
        if (mShortestDurationResultMap != null) {
            mShortestDurationResultMap.put(mode, onShortestDurationResultListener);
        }
        simpleSearch(mode, latFrom, lngFrom, latTo, lngTo);
    }

    public interface OnShortestDurationResultListener {
        /**
         * 时间最短的路径
         */
        void onShortestDurationResult(@NonNull RouteLine shortestRoute);
    }

    /**
     * 在Activity、Fragment或View生命周期结束时调用
     */
    public void destroy() {
        try {
            if (mSearch != null) {
                mSearch.destroy();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            mSearch = null;
            mResultListener = null;
            mShortestDurationResultMap = null;
        }
    }

}
